﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace HRX.PayCenter.Contract
{
    /// <summary>
    /// 公共(通用)安全类库
    /// </summary>
    public class CommonSecurity
    {
        /// <summary>
        /// 签名字段
        /// </summary>
        public const string SIGN = "sign";

        /// <summary>
        /// 获取签名内容（公共契约）
        /// </summary>
        /// <param name="dictionary"></param>
        /// <returns></returns>
        public static string GetSignContent(IDictionary<string, string> dictionary)
        {
            // 第一步：把字典按Key的字母顺序排序
            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(dictionary);
            IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();

            // 第二步：把所有参数名和参数值串在一起
            StringBuilder query = new StringBuilder("");
            while (dem.MoveNext())
            {
                string key = dem.Current.Key;
                string value = dem.Current.Value;
                if (key == SIGN)
                {
                    continue;
                } //忽略签名参数
                if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
                {
                    query.Append(key.ToLower()).Append("=").Append(value).Append("&");
                }
            }
            //var checkContent = string.Join("&", sortedParams.Select(d => string.Concat(d.Key, "=", d.Value.UrlDecode())));

            if (query.Length == 0)
            {
                return "";
            }

            return query.ToString().Substring(0, query.Length - 1);
        }

        /// <summary>
        /// 签名验证
        /// </summary>
        /// <param name="sign1"></param>
        /// <param name="sign2"></param>
        /// <returns></returns>
        public static bool SignVaild(string signA, string signB)
        {
            if (string.IsNullOrEmpty(signA) || string.IsNullOrEmpty(signB))
            {
                return false;
            }

            if (signA.Trim() != signB.Trim())
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 获取MD5加密值
        /// k1=v1&k2=v2……kn=vn
        /// </summary>
        /// <param name="input"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string SignWithMD5(string input, Encoding encoding = null)
        {
            if (string.IsNullOrEmpty(input))
                throw new ArgumentNullException("input", "MD5加密的字符串不能为空！");

            if (encoding == null)
                encoding = Encoding.UTF8;

            var data = encoding.GetBytes(input);
            var encryData = EncryptWithMD5(data);

            var sBuilder = new StringBuilder(encryData.Length * 2);
            for (var i = 0; i < encryData.Length; i++)
            {
                sBuilder.Append(encryData[i].ToString("x2"));
            }

            return sBuilder.ToString();
        }

        /// <summary>
        /// 获取MD5加密值
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private static byte[] EncryptWithMD5(byte[] bytes)
        {
            if (bytes == null || bytes.Length == 0)
                throw new ArgumentNullException("bytes", "MD5加密的字节不能为空！");

            using (var md5Hash = MD5.Create())
            {
                return md5Hash.ComputeHash(bytes);
            }
        }

        /// <summary>
        ///  AES加密
        /// </summary>
        /// <param name="toEncrypt"></param>
        /// <param name="key"></param>
        /// <param name="encoding">加密编码方式    默认为   utf-8  </param>
        /// <returns></returns>
        public static string EncryptWithAES(string toEncrypt, string key, Encoding encoding = null)
        {
            var result = string.Empty;

            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key", "key值不能为空");

            if (string.IsNullOrEmpty(toEncrypt))
                return result;

            if (encoding == null)
                encoding = Encoding.UTF8;

            var keyArray = encoding.GetBytes(key); // ToByte(key);
            var toEncryptArray = encoding.GetBytes(toEncrypt);
            var resultArray = EncryptWithAES(keyArray, toEncryptArray);
            result = Convert.ToBase64String(resultArray);

            return result;
        }

        /// <summary>
        ///  AES加密
        /// </summary>
        /// <param name="keyArray"></param>
        /// <param name="toEncryptArray"></param>
        /// <param name="iv">偏移量</param>
        /// <param name="keySize">key大小</param>
        /// <param name="blockSize">块大小</param>
        /// <param name="cipherMode"></param>
        /// <param name="paddingMode"></param>
        /// <returns></returns>
        private static byte[] EncryptWithAES(byte[] keyArray, byte[] toEncryptArray, byte[] iv = null, int keySize = 256, int blockSize = 128, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            using (var rDel = Aes.Create())
            {
                rDel.KeySize = keySize;
                rDel.BlockSize = blockSize;

                rDel.Key = keyArray;
                if (iv != null)
                {
                    rDel.IV = iv;
                }

                rDel.Mode = cipherMode;
                rDel.Padding = paddingMode;

                var cTransform = rDel.CreateEncryptor();
                var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                return resultArray;
            }
        }

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="toDecrypt"></param>
        /// <param name="key"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string DecryptWithAES(string toDecrypt, string key, Encoding encoding = null)
        {
            var result = string.Empty;

            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key", "key值不能为空");

            if (string.IsNullOrEmpty(toDecrypt))
                return result;

            if (encoding == null)
                encoding = Encoding.UTF8;

            var keyArray = encoding.GetBytes(key); // ToByte(key);
            var toEncryptArray = Convert.FromBase64String(toDecrypt); // ToByte(toDecrypt);
            var resultArray = DecryptWithAES(keyArray, toEncryptArray);
            result = encoding.GetString(resultArray);

            return result;
        }

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="keyArray">key的字节流</param>
        /// <param name="toEncryptArray">加密串的字节流</param>
        /// <param name="iv">偏移量</param>
        /// <param name="keySize">key大小</param>
        /// <param name="blockSize">块大小</param>
        /// <param name="cipherMode"></param>
        /// <param name="paddingMode"></param>
        /// <returns></returns>
        public static byte[] DecryptWithAES(byte[] keyArray, byte[] toEncryptArray, byte[] iv = null, int keySize = 256, int blockSize = 128, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            using (var rDel = Aes.Create())
            {
                rDel.KeySize = keySize;
                rDel.BlockSize = blockSize;

                rDel.Mode = cipherMode;
                rDel.Padding = paddingMode;

                rDel.Key = keyArray;
                if (iv != null)
                {
                    rDel.IV = iv;
                }

                var cTransform = rDel.CreateDecryptor();
                var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                return resultArray;
            }
        }
    }
}
